const shader = require('../shader/index');

module.exports = class ConstTextureLayer {
    // option: {
    //     id: 'starBackground',
    //     gl: gl,
    //     globe: this,
    //     type: 'texture',
    //     radius: this.radius,
    //     isTiles: false,
    //     isTileRender: false,
    //     textureUrl: 'static/images/base.png'
    // }
    constructor(option) {
        // init config
        this.id = option.id;
        this.gl = option.gl;
        const gl = option.gl;
        this.globe = option.globe;
        this.radius = option.radius;
        this.textureUrl = option.textureUrl;

        // init shader
        const program = gl.createProgram();
        this.program = program;
        const fragmentShader = gl.createShader(gl.FRAGMENT_SHADER);
        gl.shaderSource(fragmentShader, shader.tileImage.fragmentSource);
        gl.compileShader(fragmentShader);
        gl.attachShader(program, fragmentShader);
        const vertexShader = gl.createShader(gl.VERTEX_SHADER);
        gl.shaderSource(vertexShader, shader.tileImage.vertexSource);
        gl.compileShader(vertexShader);
        gl.attachShader(program, vertexShader);
        gl.linkProgram(program);

        // get attribLocation
        this.attribLocation = {
            a_Position : gl.getAttribLocation(program, 'a_Position'),
            a_TexCoord : gl.getAttribLocation(program, 'a_TexCoord'),
            a_Normal : gl.getAttribLocation(program, 'a_Normal')
        }

        // get uniformLocation
        this.uniformLocation = {
            u_ModelMatrix: gl.getUniformLocation(program, 'u_ModelMatrix'),
            u_ViewMatrix: gl.getUniformLocation(program, 'u_ViewMatrix'),
            u_ProjMatrix: gl.getUniformLocation(program, 'u_ProjMatrix'),
            u_NormalMatrix: gl.getUniformLocation(program, 'u_NormalMatrix'),
            u_LightColor: gl.getUniformLocation(program, 'u_LightColor'),
            u_LightPosition: gl.getUniformLocation(program, 'u_LightPosition'),
            u_AmbientColor: gl.getUniformLocation(program, 'u_AmbientColor'),
            u_Sampler: gl.getUniformLocation(program, 'u_Sampler')
        }

        const vertexBuffers = initVertexBuffers(gl, this.radius);
        this.buffer = {
            positionVertexBuffer: vertexBuffers.positionVertexBuffer,
            texCoordVertexBuffer: vertexBuffers.texCoordVertexBuffer,
            normalVertexBuffer: vertexBuffers.normalVertexBuffer,
            indexBuffer: vertexBuffers.indexBuffer,
            n: vertexBuffers.n
        };
        this.texture = setTextures(gl, this.textureUrl, this.globe);
    }

    render(config) {
        if (!this.texture.hasLoad) return;
        const modelMatrix = config.modelMatrix;
        const viewMatrix = config.viewMatrix;
        const projMatrix = config.projMatrix;
        const normalMatrix = config.normalMatrix;

        const gl = this.gl;
        gl.useProgram(this.program);

        gl.enable(gl.BLEND); 
        // gl.blendFunc(gl.ONE, gl.ONE_MINUS_SRC_ALPHA);
        gl.blendFunc(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA);

        gl.bindBuffer(gl.ARRAY_BUFFER, this.buffer.positionVertexBuffer);
        gl.vertexAttribPointer(this.attribLocation.a_Position, 3, gl.FLOAT, false, 0, 0);
        gl.enableVertexAttribArray(this.attribLocation.a_Position);

        gl.bindBuffer(gl.ARRAY_BUFFER, this.buffer.texCoordVertexBuffer);
        gl.vertexAttribPointer(this.attribLocation.a_TexCoord, 2, gl.FLOAT, false, 0, 0);
        gl.enableVertexAttribArray(this.attribLocation.a_TexCoord);

        gl.bindBuffer(gl.ARRAY_BUFFER, this.buffer.normalVertexBuffer);
        gl.vertexAttribPointer(this.attribLocation.a_Normal, 3, gl.FLOAT, false, 0, 0);
        gl.enableVertexAttribArray(this.attribLocation.a_Normal);

        gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, this.buffer.indexBuffer);
        
        gl.uniform3f(this.uniformLocation.u_AmbientColor, 0.2, 0.2, 0.2);
        gl.uniform3f(this.uniformLocation.u_LightColor, 1.0, 1.0, 1.0);
        gl.uniform3f(this.uniformLocation.u_LightPosition, 0.0, 0.0, 20.0);
        
        gl.uniformMatrix4fv(this.uniformLocation.u_ModelMatrix, false, modelMatrix);
        gl.uniformMatrix4fv(this.uniformLocation.u_ViewMatrix, false, viewMatrix);
        gl.uniformMatrix4fv(this.uniformLocation.u_ProjMatrix, false, projMatrix);
        gl.uniformMatrix4fv(this.uniformLocation.u_NormalMatrix, false, normalMatrix);
        
        gl.activeTexture(gl.TEXTURE0);
        gl.bindTexture(gl.TEXTURE_2D, this.texture);
        gl.uniform1i(this.uniformLocation.u_Sampler, 0);

        gl.drawElements(gl.TRIANGLES, this.buffer.n, gl.UNSIGNED_SHORT, 0);  

        gl.disable(gl.BLEND); 
    }
}

function initVertexBuffers(gl, radius) {
    let latitudeBands = 30;
    let longitudeBands = 30;

    let vertices = [];
    let normals = [];
    let texCoords = [];

    for (let latNumber = 0; latNumber <= latitudeBands; latNumber++) {
        let theta = latNumber * Math.PI / latitudeBands;
        let sinTheta = Math.sin(theta);
        let cosTheta = Math.cos(theta);

        for (let longNumber = 0; longNumber <= longitudeBands; longNumber++) {
            let phi = longNumber * 2 * Math.PI / longitudeBands;
            let sinPhi = Math.sin(phi);
            let cosPhi = Math.cos(phi);

            let x = cosPhi * sinTheta;
            let y = cosTheta;
            let z = sinPhi * sinTheta;
            let s = 1 - (longNumber / longitudeBands);
            let t = 1 - (1 - (latNumber / latitudeBands));

            normals.push(x);
            normals.push(y);
            normals.push(z);
            texCoords.push(s);
            texCoords.push(t);
            vertices.push(radius * x);
            vertices.push(radius * y);
            vertices.push(radius * z);
        }
    }

    let indices = [];
    for (let latNumber = 0; latNumber < latitudeBands; latNumber++) {
        for (let longNumber = 0; longNumber < longitudeBands; longNumber++) {
            let first = (latNumber * (longitudeBands + 1)) + longNumber;
            let second = first + longitudeBands + 1;
            indices.push(first);
            indices.push(second);
            indices.push(first + 1);

            indices.push(second);
            indices.push(second + 1);
            indices.push(first + 1);
        }
    }

    const positionVertexBuffer = gl.createBuffer();
    gl.bindBuffer(gl.ARRAY_BUFFER, positionVertexBuffer);
    gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(vertices), gl.STATIC_DRAW);

    const texCoordVertexBuffer = gl.createBuffer();
    gl.bindBuffer(gl.ARRAY_BUFFER, texCoordVertexBuffer);
    gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(texCoords), gl.STATIC_DRAW);

    const normalVertexBuffer = gl.createBuffer();
    gl.bindBuffer(gl.ARRAY_BUFFER, normalVertexBuffer);
    gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(normals), gl.STATIC_DRAW);

    const indexBuffer = gl.createBuffer();
    gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, indexBuffer);
    gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, new Uint16Array(indices), gl.STATIC_DRAW);

    return {
        positionVertexBuffer: positionVertexBuffer,
        texCoordVertexBuffer: texCoordVertexBuffer,
        normalVertexBuffer: normalVertexBuffer,
        indexBuffer: indexBuffer,
        n: indices.length
    };
}

function setTextures(gl, url, globe) {
    const texture = gl.createTexture();
    if (!texture) {
        console.log('Failed to create the texture object');
        return false;
    }
    const image = new Image();
    if (!image) {
        console.log('Failed to create the image object');
        return false;
    }
    image.onload = () => {
        gl.bindTexture(gl.TEXTURE_2D, texture);
        gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR);
        gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST);
        gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, image);
        texture.hasLoad = true;
        // globe.updateSence();
    };
    image.src = url;
    return texture
}
